package de.uni_mannheim.informatik.ai.dame_ai.dame.application;


import java.io.File;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JComponent;

import de.uni_mannheim.informatik.ai.dame_ai.dame.domain.Board;
import de.uni_mannheim.informatik.ai.dame_ai.dame.domain.Checkers;
import de.uni_mannheim.informatik.ai.dame_ai.dame.domain.GameLogic;
import de.uni_mannheim.informatik.ai.dame_ai.dame.domain.GameStatus;
import de.uni_mannheim.informatik.ai.dame_ai.dame.domain.Move;
import de.uni_mannheim.informatik.ai.dame_ai.dame.domain.Player;
import de.uni_mannheim.informatik.ai.dame_ai.main.application.GameSettings;
import de.uni_mannheim.informatik.ai.dame_ai.main.application.MainPlayer;
import de.uni_mannheim.informatik.ai.dame_ai.util.XmlElement;


public class DameFacade extends Observable implements Observer{
	protected Checkers game;
	protected ArrayList<AIPlayer> aiPlayers = new ArrayList<AIPlayer>();
	protected int selectedPosition;
	protected Move[] moves;
	protected ArrayList<Move> madeMoves = new ArrayList<Move>();
	protected ArrayList<GameStatus> states;
	//protected int possibleMoves;
	protected ArrayList<Observer> observers;
	protected DamePersistor persistor = new DamePersistor();
	protected int replayIndex;
	protected int loadIndex=0;
	boolean isReplay;
	Player[] players;
	ReplayThread replayThread;
	GameSettings data;
	public DameFacade(Player[] players, GameSettings data){
		this.players = players;
		this.data = data;
		this.isReplay = data.isReplay();
		observers = new ArrayList<Observer>();
		if(data.isLoadGame()){
			this.loadGameFrom(data.getFilePath());	
		} else if(data.isReplay()){
			startReplay();
		} else {
			startPlayCheckers(players);
		}
	}
	
	public void startReplay(){
		replayIndex = 0;
		replayThread = new ReplayThread(this);
		states = new ArrayList<GameStatus>();
		XmlElement root = persistor.getRoot(data.getFilePath());
		ArrayList<Move> temp = persistor.getMoves(root);
		moves = temp.toArray(new Move[temp.size()]);
		game = new Checkers(players, data);	
		game.addObserver(this);
		replayThread.start();
	}
	
	synchronized public boolean nextMove(){
		if(replayIndex<moves.length){
			makeMove(moves[replayIndex]);
			replayIndex++;
			return true;
		} else {
			return false;
		}
	}
	synchronized public boolean toggleAutomaticReplay(){
		boolean running = replayThread.getRunning();
		replayThread.setRunning(!running);
		System.err.println("Toggeling thread");
		return !running;
	}
	synchronized public boolean lastMove(){
		if(replayIndex>0){
			replayIndex--;
			GameStatus temp = states.get(states.size()-2);
			states.remove(states.size()-1);
			states.remove(states.size()-1);
			game.setGameStatus(temp);
			return true;
		} else return false;
	}
	/**
	 * Method initialized Domain
	 */
	private void startPlayCheckers(Player[] players){
		
		for(int i =0; i< players.length;i++){
			if(!players[i].isHuman()){
				int opponent = 0;
				if(i==0){
					opponent= 1;
				} else if (i==1){
					opponent = 0;
				}
				AIPlayer aiPlayer = new AIPlayer(players[i],players[opponent],this);
				addObserver(aiPlayer);
				aiPlayers.add(aiPlayer);
			}
		}
		System.out.println("gui created");
		game = new Checkers(players, data);	
		game.addObserver(this);
		System.out.println("observer added");
	}
	boolean skipNotify = false;
	synchronized public boolean saveGame(File destFile){
		boolean success = persistor.persist(data, madeMoves, destFile);	
		return success;
	}
	synchronized public boolean loadGameFrom(File destFile) {
		boolean success=false;
		XmlElement root = persistor.getRoot(destFile);
		ArrayList<Move> oldMoves = null;
		GameSettings newData = null;
		if(root != null) {
			oldMoves = persistor.getMoves(root);
			newData = persistor.getGameSettings(root);
			skipNotify = true;
			this.game = new Checkers(players, newData);
			game.addObserver(this);
			for(int i = 0; i< oldMoves.size();i++){
				game.makeMove(oldMoves.get(i));
			}
			madeMoves = oldMoves;
			loadIndex = madeMoves.size()-1;
			skipNotify = false;
			setNewData(newData);
			notifyObservers();
			success = true;
		}
		
		return success;
	}
	private void stopThreads(){
		for(AIPlayer player : aiPlayers){
			player.setStop(true);
		}
		if(replayThread != null){
			replayThread.setStop(true);
		}
	}
	private void restartAsReplay(){
		stopThreads();
		skipNotify = true;
		data.setReplay(true);
		data.setLoadGame(false);
		this.isReplay = true;
		replayIndex = 0;
		replayThread = new ReplayThread(this);
		states = new ArrayList<GameStatus>();
		moves = madeMoves.toArray(new Move[madeMoves.size()]);
		game = new Checkers(players, data);	
		game.addObserver(this);
		skipNotify = false;
		notifyObservers();
		replayThread.start();
	}
	private void restartAsReplay(File file){
		stopThreads();
		XmlElement root = persistor.getRoot(file);
		ArrayList<Move> oldMoves = null;
		GameSettings newData = null;
		if(root != null) {
			oldMoves = persistor.getMoves(root);
			newData = persistor.getGameSettings(root);	
			data = newData;
			MainPlayer[] mainPlayers = data.getPlayers();
			Player[] players = getPlayers(mainPlayers);
			skipNotify = true;
			data.setReplay(true);
			data.setLoadGame(false);
			this.isReplay = true;
			replayIndex = 0;
			replayThread = new ReplayThread(this);
			states = new ArrayList<GameStatus>();
			moves = oldMoves.toArray(new Move[oldMoves.size()]);
			game = new Checkers(players, data);	
			game.addObserver(this);
			skipNotify = false;
			notifyObservers();
			replayThread.start();
		}
		
	}
	private Player[] getPlayers(MainPlayer[] mainPlayers){
		Player[] players = new Player[2];
		players[0]= new Player(mainPlayers[0].getNumber(),mainPlayers[0].getNumber(),mainPlayers[0].getName(),mainPlayers[0].isHuman());
		players[1]= new Player(mainPlayers[1].getNumber(),mainPlayers[1].getNumber(),mainPlayers[1].getName(),mainPlayers[1].isHuman());
		return players;
	}
	synchronized public void setNewData(GameSettings data){
		if(data.equals(this.data)){
			
		}else{
			MainPlayer[] mainPlayers = data.getPlayers();
			Player[] players = getPlayers(mainPlayers);
			for(AIPlayer player: aiPlayers){
				player.setStop(true);
				deleteObserver(player);
			}
			aiPlayers.clear();
			for(int i =0; i< players.length;i++){
				if(!players[i].isHuman()){
					int opponent = 0;
					if(i==0){
						opponent= 1;
					} else if (i==1){
						opponent = 0;
					}
					AIPlayer aiPlayer = new AIPlayer(players[i],players[opponent],this);
					aiPlayers.add(aiPlayer);
					addObserver(aiPlayer);
				}
			}
		}
	}
	private void resetAiPlayers(){
		for(AIPlayer player: aiPlayers){
			player.setStop(true);
			deleteObserver(player);
		}
		aiPlayers.clear();
		for(int i =0; i< players.length;i++){
			if(!players[i].isHuman()){
				int opponent = 0;
				if(i==0){
					opponent= 1;
				} else if (i==1){
					opponent = 0;
				}
				AIPlayer aiPlayer = new AIPlayer(players[i],players[opponent],this);
				addObserver(aiPlayer);
				aiPlayers.add(aiPlayer);
			}
		}
	}
	@Override
	public synchronized void deleteObserver(Observer o) {
		observers.remove(o);
	}
	synchronized public void quickSave(){
		loadIndex = madeMoves.size()-1;
	}
	synchronized public void restart(){
		skipNotify = true;
		this.game = new Checkers(players, data);
		game.addObserver(this);
		madeMoves.clear();
		resetAiPlayers();
		loadIndex =0;
		skipNotify = false;
		notifyObservers();
	}
	synchronized public void quickLoad(){
		skipNotify = true;
		this.game = new Checkers(players, data);
		game.addObserver(this);
		for(int i = 0; i<= loadIndex;i++){
			game.makeMove(madeMoves.get(i));
		}
		while(madeMoves.size()>loadIndex+1){
			madeMoves.remove(loadIndex+1);
		}
		skipNotify = false;
		this.notifyObservers();
	}
	@Override
	public synchronized void addObserver(Observer o) {
		// TODO Auto-generated method stub
		observers.add(o);
		notifyObservers();
	}

	@Override
	public void notifyObservers() {
		ListIterator<Observer> it = observers.listIterator();
		while(it.hasNext()){
			it.next().update(this, result);
		}
	}
	/*
	public short[] getPieceStatus() {
		short[] pieceStatus = new short[Checkers.BOARDSQUARES];
		Piece[] pieces = game.getPieceStatus();
		for (int i = 0; i < pieceStatus.length; i++) {
			pieceStatus[i] = pieces[i].getType();
		}
		return pieceStatus;
	}*/
	/**
	 * Deselects all highlighted squares on board
	 */
	public void deselect(){
		game.deselect();
	}
	/*
	public boolean[] getHighlightedSquares() {
		boolean[] highlightedSquares = new boolean[Checkers.BOARDSQUARES];
		Square[] squares = game.getBoard();
		for (int i = 0; i < highlightedSquares.length; i++) {
			highlightedSquares[i] = squares[i].isHighlighted();
		}
		return highlightedSquares;
	}*/
	/*
	public int getCurrentColor() {
		if (game.getCurrentPlayer() == 1) {
			return Piece.WHITE;
		} else {
			return Piece.BLACK;
		}
	}*/
	
	/**
	 * @param position The position of the selected piece
	 * @param target The target position of the selected piece
	 * @return return if move possible
	 */
	public boolean isPossibleMove(int position, int target){
		System.out.println("Checking if move is possible");
		if(data.isReplay()){
			return false;
		}else return GameLogic.isPossible(game.getBoard().getPiecesStatus(), new Move(position,target));
	}
	/**
	 * Method executes Move
	 * @param position The position of the selected piece
	 * @param target The target position of the selected piece
	 */
	public void makeMove(int from,int target) {
		makeMove(new Move(from,target));
		
	}

	synchronized public void makeMove(Move move){
		if(game.makeMove(move)){
			madeMoves.add(move);
		}
		
	}
	private Object result;
	/**
	 * Method selected a piece for which possible moves will be displayed
	 * @param position Position of Board where piece is located
	 */
	public void selectPiece(int position) {
		game.selectPiece(position);
		
	}
	@Override
	public void update(Observable arg0, Object arg1) {
		result = arg1;
		if(isReplay && result instanceof GameStatus){
			GameStatus temp = (GameStatus)arg1;
			Board newBoard = Board.copyBoard(temp.getBoard());
			GameStatus newStatus = new GameStatus(newBoard,temp.getCurrentPlayer(),temp.isSelectionPossible(),temp.getSelectedPiece(), temp.getData());
			states.add(newStatus);
			
		}
		if(!skipNotify){
			if(!this.isReplay)persistor.autoSave(data, madeMoves);
			notifyObservers();
		}
		
	}
	
	public void replayCurrent() {
		
		this.restartAsReplay();		
	}
	public void replaySaved(File destFile) {
		this.restartAsReplay(destFile);
		
	}
}
